#!/usr/bin/env python3
from argparse import ArgumentParser
import os
import json
import re
import subprocess
import shlex


def actorFile(actor: str, build: str, src: str):
    res = actor.replace(build, src, 1)
    res = res.replace("actor.g.cpp", "actor.cpp")
    return res.replace("actor.g.h", "actor.h")


def rreplace(s, old, new, occurrence=1):
    li = s.rsplit(old, occurrence)
    return new.join(li)


def actorCommand(cmd: str, build: str, src: str):
    r1 = re.compile("-c (.+)(actor\.g\.cpp)")
    m1 = r1.search(cmd)
    if m1 is None:
        return cmd
    cmd1 = r1.sub("\\1actor.cpp", cmd)
    return rreplace(cmd1, build, src)


parser = ArgumentParser(
    description="Generates a new compile_commands.json for rtags+flow"
)
parser.add_argument("-b", help="Build directory", dest="builddir", default=os.getcwd())
parser.add_argument("-s", help="Build directory", dest="srcdir", default=os.getcwd())
parser.add_argument("-o", help="Output file", dest="out", default="processed_compile_commands.json")
parser.add_argument("-ninjatool", help="Ninja tool", dest="ninjatool", default="")
parser.add_argument("input", help="compile_commands.json", default="compile_commands.json", nargs="?")
args = parser.parse_args()

print("transform {} with build directory {}".format(args.input, args.builddir))

with open(args.input) as f:
    cmds = json.load(f)

swiftCompilationCommands = {}

if len(args.ninjatool) > 0:
    print("aquiring Swift compile commands using {}".format(args.ninjatool))
    try:
        ninjaInvocation = subprocess.run([args.ninjatool, "-t", "compdb"], cwd=args.builddir, capture_output=True)
        ninjaCMDs = json.loads(ninjaInvocation.stdout.decode('utf-8'))
        for fileCmd in ninjaCMDs:
            file = fileCmd['file']
            if not file.endswith('.swift'):
                continue
            cmd = fileCmd['command']
            # Skip invocations that are used to emit the header file for C++.
            if cmd.find('-emit-clang-header') != -1:
                continue

            # Cleanup '&&' and ':' tokens from command generated by Ninja.
            transformedSubCmd = [x for x in shlex.split(cmd) if x != '&&' and x != ':']
            cmd = shlex.join(transformedSubCmd)

            # Figure out which files are actually using this command.
            filesInCmd = [x for x in transformedSubCmd if x.endswith('.swift')]
            for cmdFile in filesInCmd:
                swiftCompilationCommands[cmdFile] = {'file': cmdFile, 'command': cmd, 'directory': fileCmd['directory']}
    except:
        print("error: failed to aquire Swift compilation commands")

result = []

for cmd in cmds:
    additional_flags = ["-Wno-unknown-attributes"]
    cmd["command"] = cmd["command"].replace(
        " -DNO_INTELLISENSE ", " {} ".format(" ".join(additional_flags))
    )
    if cmd["file"].endswith("actor.g.cpp"):
        # here we need to rewrite the rule
        cmd["command"] = actorCommand(cmd["command"], args.builddir, args.srcdir)
        cmd["file"] = actorFile(cmd["file"], args.builddir, args.srcdir)
        result.append(cmd)
    elif cmd['file'].endswith('.swift'):
        if cmd['file'] in swiftCompilationCommands:
            result.append(swiftCompilationCommands[cmd['file']])
    else:
        result.append(cmd)

with open(args.out, "w") as f:
    json.dump(result, f, indent=4)
